package com.wangcanxuan.www.proxy;

import com.wangcanxuan.www.annotation.exception.ExceptionHandler;
import com.wangcanxuan.www.annotation.log.LoggerHandler;
import com.wangcanxuan.www.util.LoggerUtil;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.HashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.logging.Logger;

/**
 * 我的日志代理接口
 *
 * @author HHLJ
 * @date 2022/03/18
 */
public class LogInvocationHandler implements InvocationHandler {

    /**
     * 日志记录器
     */
    private final Logger logger = LoggerUtil.getLogger();
    /**
     * 代理目标
     */
    private Object target;
    /**
     * 代理对象
     */
    private Object proxy;
    /**
     * 使用map来存储不同的InvocationHandler对象，避免生成过多
     */
    private static final HashMap<Class<?>, LogInvocationHandler> INVOCATION_HANDLER_HASH_MAP = new HashMap<>();

    private static final Lock WRITE_LOCK = new ReentrantReadWriteLock().writeLock();

    @SuppressWarnings("unchecked")
    public static<T> T getProxyInstance(Class<T> clazz) {
        WRITE_LOCK.lock();
        try {
            LogInvocationHandler invocationHandler = INVOCATION_HANDLER_HASH_MAP.get(clazz);
            if (invocationHandler == null) {
                invocationHandler = new LogInvocationHandler();
                try {
                    T tar = clazz.newInstance();
                    invocationHandler.setTarget(tar);
                    invocationHandler.setProxy(Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),invocationHandler));
                } catch (InstantiationException | IllegalAccessException e) {
                    e.printStackTrace();
                }
                INVOCATION_HANDLER_HASH_MAP.put(clazz, invocationHandler);
            }
            return (T) invocationHandler.getProxy();
        } finally {
            WRITE_LOCK.unlock();
        }
    }


    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = null;
        if (method.isAnnotationPresent(ExceptionHandler.class)) {
            begin(method, args);
            try {
                result = method.invoke(target, args);
            } catch (Exception e) {
                exception(method, e);
                return null;
            }
            end(method,result);
        }

        if (method.isAnnotationPresent(LoggerHandler.class)) {
            begin(method, args);
            result = method.invoke(target, args);
            begin(method, args);
        }
        return result;
    }

    private LogInvocationHandler() {}

    private void setTarget(Object target) {
        this.target = target;
    }

    private Object getProxy() {
        return proxy;
    }

    private void setProxy(Object proxy) {
        this.proxy = proxy;
    }

    private void begin(Method method, Object[] args){
        logger.info("位置：" + target.getClass().getName() + " 方法:" + method.getName() + " 启动" + " 方法参数: " +
                (null == args ? "null" : Arrays.asList(args).toString()));
    }

    private void end(Method method, Object result) {
        logger.info("位置：" + target.getClass().getName() + " 方法:" +
                method.getName() + " 结束" + " 返回参数:" + result);
    }

    private void exception(Method method,Exception e) {
        logger.severe("位置：" + target.getClass().getName() + " 方法:" + method.getName() + " 发生了异常：" + e.getCause());
        logger.info("位置：" + target.getClass().getName() + " 方法:" +
                method.getName() + " 异常结束");
    }
}
